/*
 * File:        packetParser.h
 * Authors:     Jonathan Kotker (ee122-bw) and Shaan Mulchandani (ee122-bv)
 * Description: This header file contains the methods required to parse
 *              a message received from the network.
 */

#ifndef _PACKETPARSER_H
#define	_PACKETPARSER_H

#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define MAX(a, b) ((a) < (b) ? (b) : (a))

#include "constants.h"
#include "messages.h"
#include "packetContents.h"
#include "stringUtils.h"

#include <stdlib.h>

/*
 * Sets the name of the victim from the provided packet bytes.
 */
void set_victim_name(char* packetBytes,
        packetContents* content, int min, int max) {
    setString(content->victimName, packetBytes, min, max);
    content->victimName[EE122_MAX_NAME_LENGTH] = 0;
    if ((check_player_name(content->victimName)) != 1)
        content->flag = 1;
};

/*
 * Sets the name of the attacker from the provided packet bytes.
 */
void set_attacker_name(char* packetBytes,
        packetContents* content, int min, int max) {
    setString(content->attackerName, packetBytes, min, max);
    content->attackerName[EE122_MAX_NAME_LENGTH] = 0;
    if ((check_player_name(content->attackerName)) != 1)
        content->flag = 1;
};

/*
 * Obtains the HP/EXP from the provided packet bytes at position `x'.
 */
long int get_hp_exp(char* packetbytes, int x) {
    long int hp_exp = 0;

    hp_exp = (packetbytes[x] << 16);
    hp_exp |= (packetbytes[x + 1] << 12);
    hp_exp |= (packetbytes[x + 2] << 8);
    hp_exp |= packetbytes[x + 3];
    return hp_exp;
};

/* This function takes in the array `packetbytes' that has been
 * received from the server, and a pointer to a `packetContents'
 * struct. The purpose of the function is to strip the array and
 * assign appropriate values to the contents of the struct.
 * 
 * Assumptions: `packetbytes' is an array with indexes 0 to 9
 *              (80 bits), `length' is a field in the struct.
 * 
 * The following is the general structure of a packet:
 * content[0] = version (fixed at IPV4),
 * content[1] = upper bits of packet length,
 * content[2] = lower bits of packet length,
 * content[3] = message type, and
 * content[4] onwards = payload. */
void parsePacket(char packetbytes[], packetContents* content) {

    content->flag = 0;

    if (packetbytes[0] != 4)
        content->flag = 1;

    content->length = (packetbytes[1] << 8);
    content->length = content->length | packetbytes[2];
    if ((content->length % 4) != 0)
        content->flag = 1;

    content->messageType = packetbytes[3];

    if ((content->messageType < 0x00) || (content->messageType > 0x0b))
        content->flag = 1;

    switch (content->messageType) {
        case LOGIN_REPLY:
        {
            content->errorCode = packetbytes[4];
            if ((content->errorCode != 0) || (content->errorCode != 1))
                content->flag = 1;
            content->hp = get_hp_exp(packetbytes, 5);
            if (content->hp < 0)
                content->flag = 1;
            content->exp = get_hp_exp(packetbytes, 9);
            if (content->exp < 0)
                content->flag = 1;
            content->xLoc = packetbytes[13];
            if ((content->xLoc < 0) || (content->xLoc >= 100))
                content->flag = 1;
            content->yLoc = packetbytes[14];
            if ((content->yLoc < 0) || (content->yLoc >= 100))
                content->flag = 1;
            break;
        }

        case LOGIN_REQUEST:
        {
            set_victim_name(packetbytes, content, 4, 14);
            break;
        }

        case MOVE:
        {
            content->moveDir = packetbytes[4];
            if ((content->moveDir < 0) || (content->moveDir > 3))
                content->flag = 1;
            break;
        }

        case MOVE_NOTIFY:
        {
            set_victim_name(packetbytes, content, 4, 14);
            content->xLoc = packetbytes[14];
            if ((content->xLoc < 0) || (content->xLoc > 100))
                content->flag = 1;
            content->yLoc = packetbytes[15];
            if ((content->yLoc < 0) || (content->yLoc > 100))
                content->flag = 1;
            content->hp = get_hp_exp(packetbytes, 16);
            if (content->hp < 0)
                content->flag = 1;
            content->exp = get_hp_exp(packetbytes, 20);
            if (content->exp < 0)
                content->flag = 1;
            break;
        }

        case ATTACK:
        {
            set_victim_name(packetbytes, content, 4, 14);
            break;
        }

        case ATTACK_NOTIFY:
        {
            set_attacker_name(packetbytes, content, 4, 14);
            set_victim_name(packetbytes, content, 14, 24);
            content->damage = packetbytes[24];
            if (content->damage < 0)
                content->flag = 1;
            content->hp = get_hp_exp(packetbytes, 25);
            break;
        }

        case SPEAK:
        {
            setString(content->message, packetbytes, 4,
                    MIN(content->length, EE122_MAX_MSG_LENGTH + 4));
            content->message[EE122_MAX_MSG_LENGTH] = 0;
            if ((check_player_message(content->message)) != 1)
                content->flag = 1;
            break;
        }

        case SPEAK_NOTIFY:
        {
            set_victim_name(packetbytes, content, 4, 14);

            setString(content->message, packetbytes, 14,
                    MIN(content->length, EE122_MAX_MSG_LENGTH + 4));
            content->message[EE122_MAX_MSG_LENGTH] = 0;
            if ((check_player_message(content->message)) != 1)
                content->flag = 1;

            if (packetbytes[content->length - 1] != 0 ||
                    !(check_player_message(content->message))) {
                on_malformed_message_from_server();
                exit(EXIT_FAILURE);
            }
            break;
        }

        case LOGOUT_NOTIFY:
        {
            set_victim_name(packetbytes, content, 4, 14);
            break;
        }

        case INVALID_STATE:
        {
            content->errorCode = packetbytes[4];
            if ((content->errorCode != 0) || (content->errorCode != 1))
                content->flag = 1;
        }
    }
};

#endif  /* _PACKETPARSER_H */
